#include "RayTracer.h"
#include "VoxelOctree.h"



RayTracer::RayTracer(int x,int y)
{
	done = false;
	sizex = x;
	sizey = y;
	objects = new list<RenderObject*>();
	initRays();
	image.Create(x,y);

	for(int i = 0;i < 50;i++)
	{
		double x = (rand() % 20) -10;
		double y = 9;
		double z = (rand() % 20) - 0;
		//	objects->push_back(new Cube(Vector3d(x,y,z),1));
	}

	objects->push_back(new VoxelOctree(Vector3d(0,-64,0),64));
	//objects->push_back(new Cube(Vector3d(0,0,0),50));

	//objects->push_back(new Sphere(Vector3d(65,40,50),10));
	//objects->push_back(new Cube(Vector3d(rand() % 100 - 50,rand() % 100 - 50,rand() % 500 + 100),rand() % 30 + 10));
	//objects->push_back(new Cube(Vector3d(0,0,0),5));

	/*
	double rad = 1;
	objects->push_back(new Sphere(Vector3d(0,0,0),rad));
	

	objects->push_back(new Sphere(Vector3d(0,5,0),rad));
	objects->push_back(new Sphere(Vector3d(5,5,0),rad));

	objects->push_back(new Sphere(Vector3d(0,0,5),rad));
	objects->push_back(new Sphere(Vector3d(5,0,5),rad));

	objects->push_back(new Sphere(Vector3d(0,5,5),rad));
	objects->push_back(new Sphere(Vector3d(5,5,5),rad));
	*/

	//objects->push_back(new Sphere(Vector3d(0,0,3),rad));

	objects->push_back(new Plane(Vector3d(0,50,0),Vector3d(0,-1,0)));
	//objects->push_back(new PlanePart(Vector3d(0,0,5),Vector3d(0,0,-1),Vector3d(0,0,0),Vector3d(5,5,5),0,0,1));
	//objects->push_back(new PlanePart(Vector3d(-10,0,0),Vector3d(1,0,0),Vector3d(-10,-10,-10),Vector3d(10,10,10),0,1,0));

	rotation = Matrixd( 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 );
}


RayTracer::~RayTracer(void)
{
}

void RayTracer::rotate( double anglex , double angley, double anglez ) {

	Matrixd rotx( 1 , 0 , 0 , 0 , cos( anglex ) , -sin( anglex ) , 0 , sin( anglex ) , cos( anglex ) );
	Matrixd roty( cos( angley ) , 0 , sin( angley ) , 0 , 1 , 0 , -sin( angley ) , 0 , cos( angley ) );
	Matrixd rotz( cos( anglez ) , -sin(anglez) , 0 , sin(anglez) , cos(anglez) , 0 , 0 , 0 , 1 );

	rotation = rotx.mult( roty ).mult(rotz);
}

void RayTracer::lookAt(Vector3d pos,Vector3d target)
{
	eye = pos;
	//Vector3d toTarget = (pos-target);
	//toTarget.Normalize();
	rotate(target.x,target.y,target.z);
}

#define PI 3.14

void RayTracer::run()
{
	lookAt(Vector3d(148,-90,-90),Vector3d(0.5,0.5,0));
	//lookAt(Vector3d(0,32,32),Vector3d(0,0,0));
	TraceResult t;
	TraceResult t2;

	vector<Vector3d> lights;

	for(int i = 0;i < 10;i++)
	{
		double x = (rand() % 50) - 25;
		double y = -60;
		double z = (rand() % 50) - 25;
		lights.push_back(Vector3d(x-25,-90,z-150));	
	}

	lights.push_back(Vector3d(125,-100,150));	
	//lights.push_back(Vector3d(0,-90,0));
	//lights.push_back(Vector3d(0,-50,-50));
	//objects->push_back(new Sphere(Vector3d(0,-74,0),5));
	//eye = Vector3d(150,100,150);
	//rotate(0.6,0.6,0.6);
	//lights.push_back(eye);

	cout << "doing sumthing\n";
	for ( int y = 0; y < sizey; y++ ) {
		if(y % (sizey/2) == 0)
			cout << "halfway dun yeh\n";

		if(y % 10 == 0)
			cout << "doing da work " << "#" << y << "\n";

		for ( int x = 0; x < sizex; x++ ) {
			Vector3d dir = directions[x + y * sizex];
			rotation.mult(dir);
			Ray start(eye,dir);
			t = trace(start);

			if(t.hit)
			{
				Vector3d hit = t.point;

				double totalLight = 0;
				for(int i = 0;i < lights.size();i++)
				{
					Vector3d dstToLight(lights[i]-hit);
					double dropoff = dstToLight.magnitude();
					dstToLight.Normalize();
					hit.Normalize();

					dropoff = dropoff > 0 ? 1/(dropoff*dropoff) : 1; 
					dropoff *= 35000;
					dropoff = dropoff > 1 ? 1 : dropoff;

					Vector3d v(hit-lights[i]);
					v.Normalize();
					double s = t.normal.dot(dstToLight);
					if(s <= 0) s = 0;
					s *= dropoff;
					
					Ray shadow(t.point,dstToLight);

					t2 = trace(shadow);
					if(t2.hit)
					{
						s = 0;
					}

					//if(s > 1) s = 1;					
					totalLight += s;
				}

				totalLight /= (double)(lights.size());

				//totalLight = totalLight > 1 ? 1 : totalLight;

				int r = (((double)((t.object->color.x))) * totalLight);
				if(r > 255) r = 255;
				int g = (((double)((t.object->color.y))) * totalLight);
				if(g > 255) g = 255;
				int b = (((double)((t.object->color.z))) * totalLight);
				if(b > 255) b = 255;
				image.SetPixel(x,y,sf::Color(r,g,b,255));
			}

		}
	}
	stringstream ss;
	ss << time(0);
	ss << "out.png";
	image.SaveToFile(ss.str());
	done = true;
}

TraceResult RayTracer::trace(const Ray &ray)
{
	TraceResult closest = TraceResult::empty;
	TraceResult r;
	for(list<RenderObject*>::iterator it = objects->begin();it != objects->end();it++)
	{
		r = (*it)->collision(ray);
		if(r.hit)
		{
			if(r.distance < closest.distance && (r.distance > 0.001))
			{
				closest = r;
			}
		}
	}
	return closest;
}

void RayTracer::initRays()
{
	directions = new Vector3d[sizex*sizey];
	for ( int y = 0; y < sizey; y++ ) {
		for ( int x = 0; x < sizex; x++ ) {
			Vector3d currDirection;
			currDirection.x = ( x - (sizex / 2) );
			currDirection.y = ( y - (sizey / 2) );
			currDirection.z = 550;
			currDirection.Normalize();

			directions[x + y * sizex] = currDirection;
		}
	}
	//f.close();
}
